﻿using AutoMapper;
using SpreadsheetGear;
using Sustainalytics.ClientDW.DataAccessLayer;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.DataLayer;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Entities.ConnectionStrings;
using Sustainalytics.Entities.ProductInvolvement;
using Sustainalytics.Entities.SustainableProducts;
using Sustainalytics.Ftp;
using Sustainalytics.FTPReport.BusinessLogic.Logging;
using Sustainalytics.MongoDB.Extensions;
using Sustainalytics.Portfolios.BL;
using Sustainalytics.ScreeningTool.BL;
using Sustainalytics.Utils.Logging.Serilog;
using Sustainalytics.Utils.Logging.Serilog.Messages;
using Sustainalytics.Utils.Serilog;
using System;
using System.Collections.Generic;
using System.Configuration.Abstractions;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Tamir.SharpSsh;

namespace Sustainalytics.FTPReport.BusinessLogic
{
    public class ReportsFtpManagement : IReportsFtpManagement
    {
        private readonly IRepositoryCollection<ReportFTP> _reportFtpRepository;
        private readonly IRepositoryCollection<FtpClientReport> _ftpClientReportRepository;
        private readonly IRepositoryCollection<Account> _accountsRepository;
        private readonly IRepositoryCollection<Filter> _filtersRepository;
        private SimpleLogging __logger;
        private AesManaged aes;

        private static TimeSpan startTime;

        private static Object objFTPLock = new Object();
        private static Object lockRenameFileObj = new Object();
        private static Object lockWorkBookObj = new Object();

        private static DayOfWeek dayOfWeekSchedule; // for weekly reports

        private static string defaultDayOfWeek = "0"; //Sunday
        private bool _generationInProgress;
        private readonly FtpClientFactory ftpClientFactory;


        public ReportsFtpManagement()
        {
            var connectionString = ConfigurationManager.Instance.ConnectionStrings["ClientPlatform"].ConnectionString;

            var reportFtpRepository = new RepositoryCollection<ReportFTP>(connectionString, "ReportFTP");

            var ftpClientReportRepository = new RepositoryCollection<FtpClientReport>(connectionString, "FtpClientReport");
            var accountsRepository = new RepositoryCollection<Account>(connectionString, "Account");
            var filtersRepository = new RepositoryCollection<Filter>(connectionString, "Filter");

            _reportFtpRepository = reportFtpRepository;
            _ftpClientReportRepository = ftpClientReportRepository;
            _accountsRepository = accountsRepository;
            _filtersRepository = filtersRepository;
            aes = new AesManaged { KeySize = 128, BlockSize = 128 };
            ftpClientFactory = new FtpClientFactory();
        }

        private ExportScreening GenerateExportScreening()
        {
            __logger = new SimpleLogging();
            var clientPlatformConnectionString = ConfigurationManager.Instance.ConnectionStrings["ClientPlatform"].ConnectionString;

            var connectionStrings = new RepositoryCollection<Entities.ConnectionStrings.ConnectionStrings>(clientPlatformConnectionString).ReadAll().First();

            var companyScreeningRepository = CompanyScreeningRepository.GetCompanyScreeningRepository(clientPlatformConnectionString);
            __logger.Log($"from {nameof(ReportsFtpManagement)} Connection String: ClientPlatform {clientPlatformConnectionString}");
            __logger.Log($"from {nameof(ReportsFtpManagement)} Connection String: {ConnectionStringType.ClientDW } is {connectionStrings.ClientDW}");

            __logger.Log($"from {nameof(ReportsFtpManagement)} Connection String: {ConnectionStringType.ScreeningDW } is {connectionStrings.ScreeningDW}");

            var clientDWRepository = ClientDwRepository.GetInstance(connectionStrings.ClientDW);

            var portfolioManager = new PortfolioReadHandler(
               new PortfolioTypeCollection<Portfolio>(new RepositoryCollection<Portfolio>(clientPlatformConnectionString), new RepositoryCollection<Portfolio>(connectionStrings.ClientDW)),
               new PortfolioTypeCollection<PortfolioDetails>(new RepositoryCollection<PortfolioDetails>(clientPlatformConnectionString), new RepositoryCollection<PortfolioDetails>(connectionStrings.ClientDW)),
                new RepositoryCollection<CorporateDataBasic>(connectionStrings.ClientDW, "CorporateData"));

            var universeRepository = UniversesRepository.GetUniversesRepository(connectionStrings.ClientUniverses);

            __logger.Log($"from {nameof(ReportsFtpManagement)} Connection String: {ConnectionStringType.ClientUniverses } is {connectionStrings.ClientUniverses}");

            var filterCriteriaRepository = new FilterCriteriaRepository(clientPlatformConnectionString);

            var screeningDataRepository = ScreeningDataRepository.GetScreeningDataRepository(connectionStrings.ScreeningDW);
            return new ExportScreening(companyScreeningRepository, clientDWRepository, portfolioManager, universeRepository, filterCriteriaRepository,
                new RepositoryCollection<CorporateData>(connectionStrings.ClientDW), screeningDataRepository,
                MongoFactory.GetCollectionFromDatabase<CorporateData>(MongoFactory.GetConnectionStrings().ClientDW),
                MongoFactory.GetCollectionFromDatabase<SustainableProductsFramework>(MongoFactory.GetConnectionStrings().ProductInvolvementDW),
                MongoFactory.GetCollectionFromDatabase<ProductInvolvementFramework>(MongoFactory.GetConnectionStrings().ProductInvolvementDW)
                );
        }

        private int DaysBetween(DateTime first, DateTime recent)
        {
            int totalDays;

            TimeSpan span = recent.Subtract(first);

            totalDays = (int)span.TotalDays;
            return totalDays;
        }

        private int HoursBetween(DateTime first, DateTime recent)
        {
            int totalHours;

            TimeSpan span = recent.Subtract(first);

            totalHours = (int)span.TotalHours;
            return totalHours;
        }

        private bool SuccessiveDatesAtLeast1MonthDiffRough(DateTime first, DateTime recent)
        {
            //D2 year parameter is the most recent
            if (recent.Year > first.Year)
            {
                return true;
            }
            if (recent.Year == first.Year)
            {
                if ((recent.Month - first.Month) >= 1)
                {
                    return true;
                }
                return false;
            }
            //d2.Year < d1.year
            return false;
        }

        private bool SuccessiveDatesAtLeast3MonthDiffRough(DateTime first, DateTime recent)
        {
            //D2 year parameter is the most recent
            if (recent.Year > first.Year)
            {
                return true;
            }
            if (recent.Year == first.Year)
            {
                if ((recent.Month - first.Month) >= 3)
                {
                    return true;
                }
                return false;
            }
            //d2.Year < d1.year
            return false;
        }

        public void StartGenerating()
        {
            if (_generationInProgress)
                return;

            _generationInProgress = true;
            string dayOfWeek = string.IsNullOrEmpty(ConfigurationManager.Instance.AppSettings["DayOfWeek"]) ? defaultDayOfWeek : ConfigurationManager.Instance.AppSettings["DayOfWeek"];

            startTime = DateTime.UtcNow.AddHours(-1).TimeOfDay;

            if (!Enum.TryParse<DayOfWeek>(dayOfWeek, out dayOfWeekSchedule))
                dayOfWeekSchedule = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), defaultDayOfWeek);

            GeneratePeriodicReports(FrequencyEnum.Daily);

            DayOfWeek today = DateTime.Today.DayOfWeek;

            if (today == dayOfWeekSchedule)
            {
                GeneratePeriodicReports(FrequencyEnum.Weekly);

                GeneratePeriodicReports(FrequencyEnum.SemiMonthly);

                GeneratePeriodicReports(FrequencyEnum.Monthly);

                GeneratePeriodicReports(FrequencyEnum.Quarterly);
            }
            _generationInProgress = false;
        }

        public void GeneratePeriodicReports(FrequencyEnum recurrenceOfInterest)
        {
            string servAddr = "No address";
            int port = 0;
            string user = "No user";
            string pass = "No password";
            var generateExportScreening = GenerateExportScreening();
            try
            {
                //Collect data from the repository which is to be scheduled for Report/Filter generation at daily basis
                List<FtpClientReport> periodicReports = _ftpClientReportRepository.ReadWhere(x => x.Frequency == recurrenceOfInterest).ToList();

                if (periodicReports.Any())
                {
                    __logger.Log($"Received {periodicReports.Count} {nameof(FtpClientReport)}");

                    #region Periodic Reports Iteration

                    foreach (FtpClientReport ftpReport in periodicReports)
                    {
                        DateTime? lastGenDate = ftpReport.LastGeneratedDate;

                        switch (recurrenceOfInterest)
                        {
                            case FrequencyEnum.Daily:
                                {
                                    //If  No report yet generated = >surely will be generated now  At least 20 hours between Daily FTP Report Generation
                                    if (lastGenDate != null && HoursBetween((DateTime)lastGenDate.Value, DateTime.UtcNow) < 20)
                                    {
                                        continue;
                                    }
                                    break;
                                }

                            case FrequencyEnum.Weekly:
                                {
                                    //At least 6 days between Weekly FTP Report Generation, picked by Daily Timer
                                    if (lastGenDate != null && DaysBetween((DateTime)lastGenDate.Value, DateTime.UtcNow) < 6)
                                    {
                                        continue;
                                    }

                                    //If Weekly generation date has not come yet, it's premature to do generate
                                    if (DateTime.UtcNow.Date != GetNextEligibleSundayUtc(DateTime.UtcNow).Date)
                                    {
                                        continue;
                                    }
                                    break;
                                }

                            case FrequencyEnum.SemiMonthly:
                                {
                                    //At least 13 days between Semi-Monthly FTP Report Generation,  picked by Daily Timer
                                    if (lastGenDate != null && (DaysBetween((DateTime)lastGenDate.Value, DateTime.UtcNow) < 13))
                                    {
                                        continue;
                                    }
                                    //If SemiMonthly generation date has not come yet, it's premature to do generate
                                    if (DateTime.UtcNow.Date != GetFirstEligibleOddSundayInMonthUtc(DateTime.UtcNow).Date)
                                    {
                                        continue;
                                    }

                                    break;
                                }
                            case FrequencyEnum.Monthly:
                                {
                                    //At least 28 days (worst case February) between Semi-Monthly FTP Report Generation AND current month should be greater than last generated date month
                                    if (lastGenDate != null && (DaysBetween((DateTime)lastGenDate.Value, DateTime.UtcNow) < 28 || !SuccessiveDatesAtLeast1MonthDiffRough((DateTime)lastGenDate.Value, DateTime.UtcNow)))
                                    {
                                        continue;
                                    }

                                    //If Monthly generation date has not come yet, it's premature to do generate
                                    if (DateTime.UtcNow.Date != GetFirstEligibleSundayInMonthUtc(DateTime.UtcNow).Date)
                                    {
                                        continue;
                                    }

                                    break;
                                }
                            case FrequencyEnum.Quarterly:
                                {
                                    //At least 88 (worst case minimum days: Febr month + 2 x 30 days months) days between Semi-Monthly FTP Report Generation AND current month - month generated > 3
                                    if (lastGenDate != null && (DaysBetween((DateTime)lastGenDate.Value, DateTime.UtcNow) < 88 || !SuccessiveDatesAtLeast3MonthDiffRough((DateTime)lastGenDate.Value, DateTime.UtcNow)))
                                    {
                                        continue;
                                    }

                                    //If Quarterly generation date has not come yet, it's premature to do generate
                                    if (DateTime.UtcNow.Date != GetFirstEligibleSundayInQuarterUtc(DateTime.UtcNow).Date)
                                    {
                                        continue;
                                    }

                                    break;
                                }

                            default:
                                {
                                    var ex = new Exception("Invalid reccurence specified for FTP Report generation.");
                                    EmailPlatformSupportWithErrorDetails(user, pass, servAddr, port, "Invalid reccurence", ex);
                                    continue;
                                }
                                break;
                        }

                        string pathDest = String.Empty;
                        string pathCombined = String.Empty;

                        Guid reportId = ftpReport.ReportId;
                        Guid accountId = ftpReport.AccountId;

                        Guid tempDirGuid = Guid.NewGuid();
                        string tempGuidString = tempDirGuid.ToString();

                        string resultTempFolder = ConfigurationManager.Instance.AppSettings["uploadFolder"];
                        pathCombined = Path.Combine(resultTempFolder, tempGuidString);
                        Console.WriteLine(pathCombined);
                        Console.WriteLine(resultTempFolder);
                        //Server Settings preserved for accountId are needed for Upload & Archive of old .xls files...

                        List<Account> accountEntities = _accountsRepository.ReadWhere(x => x.Id == accountId).ToList();

                        __logger.Log($"Received {accountEntities.Count} of {nameof(Account)}");

                        List<Filter> reportEntities = _filtersRepository.ReadWhere(x => x.Id == reportId).ToList();
                        __logger.Log($"Received {reportEntities.Count} of {nameof(Filter)}");
                        List<ReportFTP> ftpServerSettingsItems = _reportFtpRepository.ReadWhere(x => x.AccountId == accountId).ToList();
                        __logger.Log($"Received {ftpServerSettingsItems.Count} of {nameof(ReportFTP)}");
                        if (ftpServerSettingsItems.Any())
                        {
                            ReportFTP ftpSett = ftpServerSettingsItems.FirstOrDefault();
                            ReportFTPDto ftpSettDto = ReportFTPDto.ToReportFTPDto(ftpSett);

                            servAddr = ftpSettDto.ServerAddress;
                            port = ftpSettDto.ServerPort;
                            user = ftpSettDto.Username;

                            pass = DecryptAES128(ftpSett);
                        }

                        if (!accountEntities.Any())
                        {
                            //Very improbable situation where the entire Account is deleted

                            StringBuilder accountBuilder = new StringBuilder();
                            accountBuilder.Append("Could not generate ");
                            accountBuilder.Append(recurrenceOfInterest.ToString());
                            accountBuilder.Append(" FTP Reports.");
                            accountBuilder.Append(Environment.NewLine);
                            accountBuilder.Append("Account name missing for Id: ");
                            accountBuilder.Append(accountId);
                            accountBuilder.Append("!  ");
                            accountBuilder.Append(Environment.NewLine);

                            var ftpEx = new FtpClientReportMandatoryDataMissingException(accountBuilder.ToString());
                            EmailPlatformSupportWithErrorDetails(user, pass, servAddr, port, "No File name", ftpEx);
                            return;
                        }

                        Account accountEntity = accountEntities.FirstOrDefault();
                        string accountName = accountEntity.Name;

                        if (accountEntity != null && !(accountEntity.Status == Status.Active))
                        {
                            //Handle situtation of inactive/unknown states for accounts

                            StringBuilder accountInactiveBuilder = new StringBuilder();
                            accountInactiveBuilder.Append("Could not generate ");
                            accountInactiveBuilder.Append(recurrenceOfInterest.ToString());
                            accountInactiveBuilder.Append(" FTP Reports.");
                            accountInactiveBuilder.Append(Environment.NewLine);
                            accountInactiveBuilder.Append("Account: ");
                            accountInactiveBuilder.Append(accountName);
                            accountInactiveBuilder.Append(" is not Active!  ");
                            accountInactiveBuilder.Append(Environment.NewLine);

                            var ftpEx = new FtpClientReportMandatoryDataMissingException(accountInactiveBuilder.ToString());
                            EmailPlatformSupportWithErrorDetails(user, pass, servAddr, port, "No File name", ftpEx);
                            return;
                        }

                        if (!reportEntities.Any())
                        {
                            //This is the situation where a Report/Filter has been deleted for an account, which is highly probable

                            StringBuilder reportFilterBuilder = new StringBuilder();
                            reportFilterBuilder.Append("Could not generate ");
                            reportFilterBuilder.Append(recurrenceOfInterest.ToString());
                            reportFilterBuilder.Append(" FTP Reports for Account: ");
                            reportFilterBuilder.Append(accountName);
                            reportFilterBuilder.Append(Environment.NewLine);
                            reportFilterBuilder.Append("Report/Filter name missing for Id: ");
                            reportFilterBuilder.Append(reportId);
                            reportFilterBuilder.Append("!  ");
                            reportFilterBuilder.Append(Environment.NewLine);

                            var ftpEx = new FtpClientReportMandatoryDataMissingException(reportFilterBuilder.ToString());
                            EmailPlatformSupportWithErrorDetails(user, pass, servAddr, port, "No File name", ftpEx);
                            continue;
                        }

                        Filter filterEntity = reportEntities.FirstOrDefault();
                        string filterReportName = filterEntity.Name;

                        if (ftpServerSettingsItems.Any())
                        {
                            ReportFTP ftpServerSettings = ftpServerSettingsItems.FirstOrDefault();
                            ReportFTPDto ftpSettingsDto = ReportFTPDto.ToReportFTPDto(ftpServerSettings);

                            string serverAddress = ftpSettingsDto.ServerAddress;
                            int serverPort = ftpSettingsDto.ServerPort;
                            string userName = ftpSettingsDto.Username;

                            string passDecrypted = DecryptAES128(ftpServerSettings);
                            ftpSettingsDto.Password = passDecrypted;

                            //Test connection to FTP Servers also for Upload operations, since Server Address and Server Port are editable fields

                            bool isValidFtpServerConnection = TestConnectionToFTPServers(ftpSettingsDto);

                            if (!isValidFtpServerConnection)
                            {
                                var accEx = new FtpReportServerAccessDeniedException("Access denied on FTP Server at address :" + serverAddress + " and port: " + ftpSettingsDto.ServerPort.ToString() + " for User Name: " + userName + " with password: " + passDecrypted);

                                EmailPlatformSupportWithErrorDetails(userName, passDecrypted, serverAddress, serverPort, "No File name", accEx);

                                continue;
                            }

                            try
                            {
                                var fileNameDest = string.Empty;
                                var year = DateTime.UtcNow.Year.ToString();
                                var month = DateTime.UtcNow.ToString("MM");
                                var day = DateTime.UtcNow.ToString("dd");

                                //Date in Report file name will be formated to: YYMMDD
                                year = year.Substring(2, 2);

                                string dateNowShort = string.Concat(year, month, day);

                                //File name convention
                                pathDest = $"Sustainalytics_{filterReportName}_{dateNowShort}";

                                if (generateExportScreening == null)
                                {
                                    Exception ex = new Exception("Export Screening to run Report/Filter Id upon is Null at this point. No byte Array to be generated for this Report/Filter Id: " + reportId);
                                    EmailPlatformSupportWithErrorDetails(userName, passDecrypted, serverAddress, serverPort, "Export Screening is null", ex);
                                    return;
                                }

                                byte[] content;
                                try
                                {
                                    __logger.Log($"Call export screening for {reportId}");

                                    Console.WriteLine("before run");
                                    content = generateExportScreening.RunScreeningExportForFtp(reportId.ToString());
                                    Console.WriteLine("afer run");
                                }
                                catch (Exception byteGenEx)
                                {
                                    Console.WriteLine(byteGenEx.ToString());

                                    StringBuilder byteGenBuilder = new StringBuilder();
                                    byteGenBuilder.Append("Error while running Screening Export for ");
                                    byteGenBuilder.Append(recurrenceOfInterest.ToString());
                                    byteGenBuilder.Append(" FTP Reports for Account: ");
                                    byteGenBuilder.Append(accountName);
                                    if (filterReportName != null && !string.IsNullOrWhiteSpace(filterReportName))
                                    {
                                        byteGenBuilder.Append(" Filter/Report name is: ");
                                        byteGenBuilder.Append(filterReportName);
                                    }
                                    byteGenBuilder.Append(Environment.NewLine);

                                    EmailPlatformSupportWithErrorDetails(user, pass, servAddr, port, pathDest, byteGenBuilder.ToString(), byteGenEx);

                                    // set LastGeneratedDate to Now also in this catch block (generation error), so that multiple error emails are avoided
                                    ModifyFtpClientReportLastAndNextGeneratedDateToNow(ftpReport);

                                    continue;
                                }

                                IWorkbookSet workbookDest = Factory.GetWorkbookSet();
                                Console.WriteLine("before read from memory");
                                var destExportWorkBook = workbookDest.Workbooks.OpenFromMemory(content);

                                Console.WriteLine("Trying to Create folder " + pathCombined);
                                Directory.CreateDirectory(pathCombined);
                                Console.WriteLine("Created folder " + pathCombined);
                                if (filterEntity.ExportType != null)
                                {
                                    lock (lockWorkBookObj)
                                    {
                                        ExportType xlsxOrCsv = filterEntity.ExportType.Value;

                                        if (xlsxOrCsv == ExportType.Excel)
                                        {
                                            fileNameDest = Path.Combine(pathCombined, pathDest + ".xlsx");

                                            destExportWorkBook.SaveAs(fileNameDest, FileFormat.OpenXMLWorkbook);
                                        }
                                        else if (xlsxOrCsv == ExportType.CSV)
                                        {
                                            fileNameDest = Path.Combine(pathCombined, pathDest + ".csv");

                                            destExportWorkBook.SaveAs(fileNameDest, FileFormat.CSV);
                                        }
                                    }
                                }

                                if (File.Exists(fileNameDest))
                                {
                                    //Upload
                                    UploadToFTPServers(ftpSettingsDto, fileNameDest, content);

                                    //Modify LastGeneratedDate to Now
                                    ModifyFtpClientReportLastAndNextGeneratedDateToNow(ftpReport);
                                }
                            }
                            catch (Exception ex)
                            {
                                CommonEventSerilog.Log.CallerError(FTPReportMessage.UpdateError, DebugHelper.GetCallerInfo(), ftpSettingsDto.Id, ftpSettingsDto.AccountId, ftpSettingsDto.ServerAddress, ftpSettingsDto.ServerPort, ftpSettingsDto.Username, ftpSettingsDto.Password, ftpSettingsDto.FtpServerType.ToString(), ex);

                                EmailPlatformSupportWithErrorDetails(user, pass, servAddr, port, pathDest, ex);

                                continue;
                            }
                        }
                        else
                        {
                            var ftpEx = new FtpReportServerAccessDeniedException("No FTP Settings persisted for Account: " + accountName);
                            CommonEventSerilog.Log.CallerError(FTPReportMessage.NoServerSettingsError, DebugHelper.GetCallerInfo(), accountName, ftpEx);

                            EmailPlatformSupportWithErrorDetails(user, pass, servAddr, port, "No FTP Settings persisted for Account: " + accountName, ftpEx);
                            return;
                        }
                    }

                    #endregion Periodic Reports Iteration
                }
            }
            catch (Exception exexex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.GenerationErrorAtReccurence, DebugHelper.GetCallerInfo(), recurrenceOfInterest.ToString(), exexex);

                EmailPlatformSupportWithErrorDetails(user, pass, servAddr, port, "No File name", exexex);
                return;
            }
        }

        private DateTime GetNextEligibleDayWithTimeUtc()
        {
            TimeSpan currentTimeOfDayUtc = DateTime.UtcNow.TimeOfDay;

            //If time of day is before 7:00 A.M and today is Sunday, it's still in time for daily Timer to enclose this report into engime generation
            if (currentTimeOfDayUtc < startTime)
            {
                return DateTime.UtcNow;
            }

            DateTime result = DateTime.UtcNow.AddDays(1);

            return result;
        }

        private DateTime GetNextEligibleSundayUtc(DateTime offsetDate)
        {
            //If time of day is before 7:00 A.M and today is Sunday, it's still in time for daily Timer to enclose this report into engime generation
            if (offsetDate.DayOfWeek == dayOfWeekSchedule)
            {
                return offsetDate;
            }

            DateTime result = offsetDate.AddDays(1);
            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);
            return result;
        }

        private DateTime GetNextEligibleSundayNextGenerationUtc(DateTime offsetDate)
        {
            TimeSpan currentTimeOfDayUtc = offsetDate.TimeOfDay;

            //If time of day is before 7:00 A.M and today is Sunday, it's still in time for daily Timer to enclose this report into engine generation
            if (offsetDate.DayOfWeek == dayOfWeekSchedule)
            {
                if (currentTimeOfDayUtc < startTime)
                {
                    return DateTime.UtcNow;
                }
                else
                {
                    offsetDate = offsetDate.AddDays(7);
                    return offsetDate;
                }
            }

            DateTime result = offsetDate.AddDays(1);
            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);
            return result;
        }

        private DateTime GetFirstEligibleSundayInMonthUtc(DateTime offsetDate)
        {
            DateTime result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);
            result = result.ToUniversalTime();

            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);

            //If the 1day of Month and time of day is before 7:00 A.M and today is Sunday, it's still in time for daily Timer to enclose this report into engine generation
            //If it's the 1st Sunday of the Month that begins the Month
            if (offsetDate.DayOfWeek == dayOfWeekSchedule && (result.Date == offsetDate.Date))
            {
                return offsetDate;
            }

            if (offsetDate.Date < result.Date)
                return result;

            if (offsetDate.Date > result.Date)
            {
                offsetDate = offsetDate.AddMonths(1);
                result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);

                while (result.DayOfWeek != dayOfWeekSchedule)
                    result = result.AddDays(1);

                return result;
            }

            return result;
        }

        private static DateTime GetFirstEligibleSundayInMonthNextGenerationUtc(DateTime offsetDate)
        {
            DateTime result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);
            result = result.ToUniversalTime();

            TimeSpan offfsetTimeOfDayUtc = offsetDate.TimeOfDay;

            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);

            //If the 1day of Month and time of day is before 7:00 A.M and today is Sunday, it's still in time for daily Timer to enclose this report into engine generation
            //If it's the 1st Sunday of the Month that begins the Month
            if (offsetDate.DayOfWeek == dayOfWeekSchedule && (result.Date == offsetDate.Date))
            {
                if (offfsetTimeOfDayUtc < startTime)
                {
                    return offsetDate;
                }
                else
                {
                    offsetDate = offsetDate.AddMonths(1);
                    result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);

                    while (result.DayOfWeek != dayOfWeekSchedule)
                        result = result.AddDays(1);

                    return result;
                }
            }

            if (offsetDate.Date < result.Date)
                return result;

            if (offsetDate.Date > result.Date)
            {
                offsetDate = offsetDate.AddMonths(1);
                result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);

                while (result.DayOfWeek != dayOfWeekSchedule)
                    result = result.AddDays(1);

                return result;
            }

            return result;
        }

        private DateTime GetFirstEligibleOddSundayInMonthUtc(DateTime offsetDate)
        {
            DateTime result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);
            result = result.ToUniversalTime();

            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);

            //If just stepped into first Sunday of Month
            if (offsetDate.DayOfWeek == dayOfWeekSchedule && (result.Date == offsetDate.Date))
            {
                //If passed time generation time => next eligible odd Sunday is considered
                return offsetDate;
            }

            if (offsetDate.Date < result.Date)
                return result;

            if (offsetDate.Date > result.Date)
            {
                result = result.AddDays(14);

                //Verify offsetDate against new odd weekend
                if ((offsetDate.Date < result.Date) || (offsetDate.Date == result.Date))
                {
                    return result;
                }
                //It's way passed the 3rd Sunday in current Month or it's 3rd Sunday , but time has passed the allowed generation time

                offsetDate = offsetDate.AddMonths(1);
                result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);

                while (result.DayOfWeek != dayOfWeekSchedule)
                    result = result.AddDays(1);

                return result;
            }

            return result;
        }

        private DateTime GetFirstEligibleOddSundayInMonthNextGenerationUtc(DateTime offsetDate)
        {
            DateTime result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);
            result = result.ToUniversalTime();

            TimeSpan offfsetTimeOfDayUtc = offsetDate.TimeOfDay;

            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);

            //If just stepped into first Sunday of Month
            if (offsetDate.DayOfWeek == dayOfWeekSchedule && (result.Date == offsetDate.Date))
            {
                //If passed time generation time => next eligible odd Sunday is considered
                if (offfsetTimeOfDayUtc < startTime)
                {
                    return offsetDate;
                }
                result = result.AddDays(14);
                return result;
            }

            if (offsetDate.Date < result.Date)
                return result;

            if (offsetDate.Date > result.Date)
            {
                result = result.AddDays(14);

                //Verify offsetDate against new odd weekend
                if ((offsetDate.Date < result.Date) || (offsetDate.Date == result.Date && offfsetTimeOfDayUtc < startTime))
                {
                    return result;
                }
                //It's way passed the 3rd Sunday in current Month or it's 3rd Sunday , but time has passed the allowed generation time

                offsetDate = offsetDate.AddMonths(1);
                result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);

                while (result.DayOfWeek != dayOfWeekSchedule)
                    result = result.AddDays(1);

                return result;
            }

            return result;
        }

        private DateTime GetFirstEligibleSundayInQuarterUtc(DateTime offsetDate)
        {
            DateTime result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);
            result = result.ToUniversalTime();

            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);

            //If it's the 1day of Month that starts a Quarter and time of day is before 7:00 A.M and also today is Sunday,
            //it's still in time for daily Timer to enclose this report into engine generation
            if (offsetDate.Month == 1 || offsetDate.Month == 4 || offsetDate.Month == 7 || offsetDate.Month == 10)
            {
                //If it's the 1st Sunday of the Month that begins the quarter
                if (offsetDate.DayOfWeek == dayOfWeekSchedule && (result.Date == offsetDate.Date))
                {
                    return offsetDate;
                }
                offsetDate = offsetDate.AddMonths(3);
            }
            else
            {
                //Calculate the number of months to be added up until next quarter
                int monthsDiff = 0;
                int offsetMonth = offsetDate.Month;

                //Quarter 1 date in between dates: must be shifted to Quarter 2
                if (offsetMonth > 1 && offsetMonth <= 3)
                {
                    monthsDiff = 4 - offsetMonth;
                }

                //Quarter 2 date in between dates: must be shifted to Quarter 3
                if (offsetMonth > 4 && offsetMonth <= 6)
                {
                    monthsDiff = 7 - offsetMonth;
                }

                //Quarter 3 date in between dates: must be shifted to Quarter 4
                if (offsetMonth > 7 && offsetMonth <= 9)
                {
                    monthsDiff = 10 - offsetMonth;
                }

                //Quarter 4 date in between dates: must be shifted to Next Year - Quarter 1
                if (offsetMonth > 10 && offsetMonth <= 12)
                {
                    monthsDiff = 13 - offsetMonth;
                }

                //Finally shift date (can shift to next year for Quarter dates that pass over October month)
                offsetDate = offsetDate.AddMonths(monthsDiff);
            }

            result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);

            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);

            return result;
        }

        private DateTime GetFirstEligibleSundayInQuarterNextGenerationUtc(DateTime offsetDate)
        {
            DateTime result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);
            result = result.ToUniversalTime();

            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);

            TimeSpan offfsetTimeOfDayUtc = offsetDate.TimeOfDay;

            //If it's the 1day of Month that starts a Quarter and time of day is before 7:00 A.M and also today is Sunday,
            //it's still in time for daily Timer to enclose this report into engine generation
            if (offsetDate.Month == 1 || offsetDate.Month == 4 || offsetDate.Month == 7 || offsetDate.Month == 10)
            {
                //If it's the 1st Sunday of the Month that begins the quarter
                if (offsetDate.DayOfWeek == dayOfWeekSchedule && (result.Date == offsetDate.Date))
                {
                    if (offfsetTimeOfDayUtc < startTime)
                    {
                        return offsetDate;
                    }
                }

                offsetDate = offsetDate.AddMonths(3);
            }
            else
            {
                //Calculate the number of months to be added up until next quarter
                int monthsDiff = 0;
                int offsetMonth = offsetDate.Month;

                //Quarter 1 date in between dates: must be shifted to Quarter 2
                if (offsetMonth > 1 && offsetMonth <= 3)
                {
                    monthsDiff = 4 - offsetMonth;
                }

                //Quarter 2 date in between dates: must be shifted to Quarter 3
                if (offsetMonth > 4 && offsetMonth <= 6)
                {
                    monthsDiff = 7 - offsetMonth;
                }

                //Quarter 3 date in between dates: must be shifted to Quarter 4
                if (offsetMonth > 7 && offsetMonth <= 9)
                {
                    monthsDiff = 10 - offsetMonth;
                }

                //Quarter 4 date in between dates: must be shifted to Next Year - Quarter 1
                if (offsetMonth > 10 && offsetMonth <= 12)
                {
                    monthsDiff = 13 - offsetMonth;
                }

                //Finally shift date (can shift to next year for Quarter dates that pass over October month)
                offsetDate = offsetDate.AddMonths(monthsDiff);
            }

            result = new DateTime(offsetDate.Year, offsetDate.Month, 1, 5, 0, 0);

            while (result.DayOfWeek != dayOfWeekSchedule)
                result = result.AddDays(1);

            return result;
        }

        public ReportFTP LoadReportFtpForAccountId(Guid accountId)
        {
            try
            {
                CommonEventSerilog.Log.Info(FTPReportMessage.GetReportForAccountId, accountId);

                List<ReportFTP> reportFtpEntities = _reportFtpRepository.ReadWhere(x => x.AccountId == accountId).ToList();
                CommonEventSerilog.Log.Info(FTPReportMessage.GetReportForAccountIdSuccess, accountId);


                if (reportFtpEntities.Any())
                {
                    ReportFTP reportFtpEntity = reportFtpEntities.FirstOrDefault();

                    //Decrypt Password
                    string decryptedPassword = DecryptAES128(reportFtpEntity);

                    reportFtpEntity.Password = decryptedPassword;

                    return reportFtpEntity;
                }

                //If more than one Ftp report is present, throw exception? what is the behavior?
                return null;
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.GetReportForAccountIdError, DebugHelper.GetCallerInfo(), accountId, ex);

                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);

                return null;
            }
        }

        public ReportFTP LoadReportFtp(Guid id)
        {
            List<ReportFTP> reportFtpEntities = _reportFtpRepository.ReadWhere(x => x.Id == id).ToList();
            if (reportFtpEntities.Any())
            {
                ReportFTP reportFtpEntity = reportFtpEntities.FirstOrDefault();

                //Decrypt Password
                string decryptedPassword = DecryptAES128(reportFtpEntity);

                reportFtpEntity.Password = decryptedPassword;

                return reportFtpEntity;
            }
            return null;
        }

        public void CreateReportFtp(ReportFTPDto ftpReportDto)
        {
            string userName = ftpReportDto.Username;
            string password = ftpReportDto.Password;

            string serverAddress = ftpReportDto.ServerAddress;
            int serverPort = ftpReportDto.ServerPort;

            if (ftpReportDto.ServerPort == 0 || ftpReportDto.ServerAddress == null || ftpReportDto.ServerAddress.Equals(String.Empty) || (int)ftpReportDto.FtpServerType == 0)
            {
                var repEx = new FtpReportServerSettingsMissingException("Missing FTP Server Settings. Server Port is: " + ftpReportDto.ServerPort + ", Server Address Id is: " + ftpReportDto.ServerAddress + ", Ftp Server Type is: " + ftpReportDto.FtpServerType.ToString());

                CommonEventSerilog.Log.CallerError(FTPReportMessage.ReportAddError, DebugHelper.GetCallerInfo(), ftpReportDto.Id, ftpReportDto.AccountId, ftpReportDto.ServerAddress, ftpReportDto.ServerPort, ftpReportDto.Username, ftpReportDto.Password, ftpReportDto.FtpServerType.ToString(), repEx);

                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, " No File Name", repEx);

                return;
            }

            if (ftpReportDto.Username == null || ftpReportDto.Username.Equals(String.Empty) || ftpReportDto.Password == null || ftpReportDto.Password.Equals(String.Empty))
            {
                var servEx = new FtpReportServerCredentialsMissingException("Missing FTP Server Credentials. User name is: " + ftpReportDto.Username + " and Password Id is: " + ftpReportDto.Password);

                CommonEventSerilog.Log.CallerError(FTPReportMessage.ReportAddError, DebugHelper.GetCallerInfo(), ftpReportDto.Id, ftpReportDto.AccountId, ftpReportDto.ServerAddress, ftpReportDto.ServerPort, ftpReportDto.Username, ftpReportDto.Password, ftpReportDto.FtpServerType.ToString(), servEx);

                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, " No File Name", servEx);

                return;
            }

            bool isValidFtpServerConnection = TestConnectionToFTPServers(ftpReportDto);

            if (!isValidFtpServerConnection)
            {
                var ex = new FtpReportServerAccessDeniedException("Access denied on FTP Server at address :" + serverAddress + " and port: " + serverPort.ToString() + " for the entered credentials.");
                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, " No File Name", ex);

                return;
            }
            try
            {
                ReportFTP reportFtpEntity = ReportFTPDto.ToReportFTPEntity(ftpReportDto);

                //Encrypt the password
                string encryptedPass = EncryptStringAES128(ftpReportDto.Password);
                reportFtpEntity.Password = encryptedPass;
                reportFtpEntity.AESKeyEncoded = Convert.ToBase64String(aes.Key.ToArray());
                reportFtpEntity.AESInitVectorEncoded = Convert.ToBase64String(aes.IV.ToArray());

                _reportFtpRepository.Create(reportFtpEntity);
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.ReportAddError, DebugHelper.GetCallerInfo(), ftpReportDto.Id, ftpReportDto.AccountId, ftpReportDto.ServerAddress, ftpReportDto.ServerPort, ftpReportDto.Username, ftpReportDto.Password, ftpReportDto.FtpServerType.ToString(), ex);

                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, " No File Name", ex);

                return;
            }
        }

        private void UploadToFTPServers(ReportFTPDto ftpReportDto, string fileName, byte[] content)
        {
            string userName = ftpReportDto.Username;
            string password = ftpReportDto.Password;
            string directoryPath = string.Empty;
            if (!string.IsNullOrWhiteSpace(ftpReportDto.DirectoryPath)) directoryPath = ftpReportDto.DirectoryPath;

            string serverAddress = ftpReportDto.ServerAddress;
            int serverPort = ftpReportDto.ServerPort;

            try
            {
                switch (ftpReportDto.FtpServerType)
                {
                    case FtpServerTypeEnum.Ftp:
                        {
                            UploadToFTPServer(userName, password, serverAddress, serverPort, fileName, content, directoryPath);
                            break;
                        }
                    case FtpServerTypeEnum.FtpSSH:
                        {
                            UploadToSSHFTPServerNew(userName, password, serverAddress, serverPort, fileName, content, directoryPath);
                            //another method for FTP SSH Protocol
                            //UploadToSSHFTPServer(userName, password, serverAddress, serverPort, fileName, directoryPath);
                            break;
                        }
                    case FtpServerTypeEnum.FtpSSL:
                        {
                            //another method for FTP/SSL Protocol
                            UploadToFTPSSLServer(userName, password, serverAddress, serverPort, fileName, content, directoryPath);
                            break;
                        }
                    default:
                        {
                            var ex = new FtpReportServerAccessDeniedException("Access denied on FTP Server at address :" + serverAddress + " and port: " + serverPort.ToString() + " for the entered credentials.");
                            EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, fileName, ex);
                            return;
                        }
                }
            }
            catch (FTPReportGenerationException ftpEx)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerAccessDeniedForCredentialsError, DebugHelper.GetCallerInfo(), serverAddress, serverPort, userName, password, ftpEx);

                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, fileName, ftpEx);

                return;
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerUploadFileError, DebugHelper.GetCallerInfo(), serverAddress, serverPort, userName, password, fileName, ex);

                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, fileName, ex);

                return;
            }
        }

        public void UploadToFTPServer(string username, string password, string serverAddress, int serverPort, string fileName, byte[] content, string directoryPath)
        {
            try
            {
                //This method should be called with EnableSsl to false, since it's FTP Server simple..
                UploadToFTPOrFPTSSLServerGeneric(username, password, serverAddress, serverPort, fileName, content, false, directoryPath);
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerUploadFileError, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, ex);

                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                return;
            }
        }

        private string AssembleValidUrlAddress(string serverAddress, int serverPort)
        {
            string url = "ftp://" + serverAddress + ":" + serverPort;

            return url;
        }

        public SortedDictionary<int, string> GetSortedFilesDatesDictionary(string fileName, List<string> fileEntries)
        {
            SortedDictionary<int, string> datesDictionary = new SortedDictionary<int, string>();

            string fileNameShort = Path.GetFileNameWithoutExtension(fileName);

            string fileExtension = Path.GetExtension(fileName);

            //Disregard the last 6 digits that indicate YYMMDD - date at generation
            string fileShortNoDate = fileNameShort.Substring(0, fileNameShort.Length - 6);

            List<string> eligibleFilesForDateSorting = new List<string>();

            var year = DateTime.UtcNow.Year.ToString();
            var month = DateTime.UtcNow.ToString("MM");
            var day = DateTime.UtcNow.ToString("dd");

            //Date in Report file name will be formated to: YYMMDD
            year = year.Substring(2, 2);

            string dateNowShort = string.Concat(year, month, day);

            //Get only files with the same Filter/Report Name for date sorting dictionary
            foreach (string fileEntry in fileEntries)
            {
                if (fileEntry.Contains(dateNowShort) || fileEntry.Equals(".") || fileEntry.Equals("..") || fileEntry.Equals("Archive") || (!fileEntry.EndsWith(".xlsx") && !fileEntry.EndsWith(".csv")))
                {
                    continue;
                }

                if (fileShortNoDate != string.Empty && fileEntry.Contains(fileShortNoDate) && fileEntry.EndsWith(fileExtension, StringComparison.Ordinal))
                {
                    eligibleFilesForDateSorting.Add(fileEntry);
                }
            }

            if (eligibleFilesForDateSorting.Count > 0)
            {
                foreach (string fileEntry in eligibleFilesForDateSorting)
                {
                    //Archive oldest before Uploading newest, to prevent archiving of newest uploaded file
                    if (fileEntry.Equals(".") || fileEntry.Equals("..") || fileEntry.Equals("Archive") || (!fileEntry.EndsWith(".xlsx") && !fileEntry.EndsWith(".csv")))
                    {
                        continue;
                    }

                    //Every 4 digit date in YYMMDD format will be converted to corresponding integer and afterwards ordered in a sorted dictionary sorted on the Key
                    string dateCreatedSection = Regex.Match(fileEntry, @"\d{6}").Value;
                    int dateCreated = Convert.ToInt32(dateCreatedSection);

                    if (!datesDictionary.Keys.Contains(dateCreated))
                    {
                        datesDictionary.Add(dateCreated, fileEntry);
                    }
                }
            }

            return datesDictionary;
        }

        private FtpWebRequest CreateFtpRequest(string rootPath, bool enableSSL, string user, string password, string method)
        {
            var request = (FtpWebRequest)WebRequest.Create(rootPath);
            request.KeepAlive = false;
            request.UsePassive = true;
            request.EnableSsl = enableSSL;
            request.Credentials = new NetworkCredential(user, password);
            request.Method = method;

            return request;
        }

        public void UploadToFTPOrFPTSSLServerGenericNew(string username, string password, string serverAddress, int serverPort, string fileName, byte[] content, bool enableSsl, string directoryPath)
        {
            try
            {
                var bufferSize = ConfigurationManager.Instance.AppSettings.AppSetting<int>("BufferSize");
                string url = AssembleValidUrlAddress(serverAddress, serverPort);

                string ftpPathToRoot = url + directoryPath;

                var ftpSettings = new FtpSettings
                {
                    Username = username,
                    Password = password,
                    Address = serverAddress,
                    Port = serverPort,
                    Protocol = Protocol.Ftp,
                    KeyFile = fileName,
                    Folder = directoryPath
                };

                var ftpClient = ftpClientFactory.TryCreate(ftpSettings);

                var files = ftpClient.ftpClient.ListDirectory(ftpPathToRoot).ToList();

                SortedDictionary<int, string> datesDictionary = GetSortedFilesDatesDictionary(fileName, files);

                if (datesDictionary.Count > 0)
                {
                    string fileSenior = datesDictionary.First().Value;

                    string ftpPathToArchive = directoryPath + "Archive/";

                    lock (objFTPLock)
                    {
                        if (!files.Contains("Archive"))
                        {
                            ftpClient.ftpClient.CreateDirectory(ftpPathToArchive);
                        }
                    }

                    lock (lockRenameFileObj)
                    {
                        var tries = 2;

                        do
                        {
                            try
                            {
                                string oldPath = directoryPath + fileSenior;
                                string newPath = "Archive/" + fileSenior;
                                ftpClient.ftpClient.RenameFile(oldPath, newPath);
                                tries--;
                            }
                            catch (WebException ex)
                            {
                                var archiveFileName = "Archive/" + fileSenior;

                                FtpWebResponse response = (FtpWebResponse)ex.Response;
                                if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
                                {
                                    EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                                    ftpClient.ftpClient.DeleteFile(archiveFileName);
                                }
                            }
                            finally
                            {
                                tries--;
                            }

                        } while (tries > 0);
                    }


                }

                lock (objFTPLock)
                {
                    string fileShort = Path.GetFileName(fileName);
                    string ftpPathToRootDir = directoryPath + fileShort;

                    Stream contentStream = new MemoryStream(content);


                    using (var ftpStream = ftpClient.ftpClient.GetRequestStream(ftpPathToRootDir))
                    {
                        contentStream.CopyTo(ftpStream, bufferSize);
                    }
                }
            }
            catch (WebException webex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerGenericUploadFileErrorListDirectoryContents, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, webex);

                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, webex);
            }
            catch (Exception ex)
            {
                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);
            }
        }

        public void UploadToFTPOrFPTSSLServerGeneric(string username, string password, string serverAddress, int serverPort, string fileName, byte[] content, bool enableSsl, string directoryPath)
        {
            try
            {
                string url = AssembleValidUrlAddress(serverAddress, serverPort);
                int nrTrialsTask = 3;

                while (nrTrialsTask != 0)
                {
                    try
                    {
                        string ftpPathToRoot = url + "//" + directoryPath;

                        //Get all files with details from FTP Server

                        var ftpRequestListDir = CreateFtpRequest(ftpPathToRoot, enableSsl, username, password, WebRequestMethods.Ftp.ListDirectory);

                        FtpWebResponse resListDir = null;

                        try
                        {

                            try
                            {
                                resListDir = (FtpWebResponse)ftpRequestListDir.GetResponse();
                            }
                            catch (WebException webex)
                            {
                                CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerGenericUploadFileErrorListDirectoryContents, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, webex);

                                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, webex);

                                return;
                            }

                            if (resListDir != null)
                            {
                                StreamReader streamReaderFiles = new StreamReader(resListDir.GetResponseStream());

                                List<string> files = new List<string>();
                                string fileLine = streamReaderFiles.ReadLine();

                                while (!string.IsNullOrEmpty(fileLine))
                                {
                                    files.Add(fileLine);
                                    fileLine = streamReaderFiles.ReadLine();
                                }

                                streamReaderFiles.Close();

                                SortedDictionary<int, string> datesDictionary = GetSortedFilesDatesDictionary(fileName, files);

                                try
                                {
                                    if (datesDictionary.Count > 0)
                                    {
                                        //Only one old file should be  present for an AccountReport combination file,
                                        //due to repetitive moves into Archive Folder at each step
                                        string fileSenior = datesDictionary.First().Value;

                                        string ftpPathToArchive = url + "//" + directoryPath + "Archive/";

                                        lock (objFTPLock)
                                        {
                                            if (!files.Contains("Archive"))
                                            {
                                                var ftpRequestMakeDir = CreateFtpRequest(ftpPathToArchive, enableSsl, username, password, WebRequestMethods.Ftp.MakeDirectory);

                                                FtpWebResponse resMakeDir = null;
                                                try
                                                {
                                                    resMakeDir = (FtpWebResponse)ftpRequestMakeDir.GetResponse();
                                                }
                                                catch (WebException ex)
                                                {
                                                    CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerGenericUploadFileErrorMakeDirectoryArchive, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, ex);

                                                    EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                                                    return;
                                                }
                                                finally
                                                {
                                                    if (resMakeDir != null)
                                                        resMakeDir.Close();
                                                }
                                            }
                                        }

                                        lock (lockRenameFileObj)
                                        {
                                            var tries = 2;
                                            do
                                            {
                                                string oldestCurrentUrl = url + "/" + directoryPath + fileSenior;

                                                var ftpRequestUploadOldest = CreateFtpRequest(oldestCurrentUrl, enableSsl, username, password, WebRequestMethods.Ftp.Rename);
                                                ftpRequestUploadOldest.RenameTo = "Archive/" + fileSenior;
                                                ftpRequestUploadOldest.UseBinary = true;

                                                FtpWebResponse resOldestUpload = null;

                                                try
                                                {
                                                    resOldestUpload = (FtpWebResponse)ftpRequestUploadOldest.GetResponse();
                                                    tries--;
                                                }
                                                catch (WebException ex)
                                                {
                                                    var archiveFileName = url + "/" + directoryPath + "Archive/" + fileSenior;
                                                    FtpWebResponse response = (FtpWebResponse)ex.Response;
                                                    if (response.StatusCode ==
                                                        FtpStatusCode.ActionNotTakenFileUnavailable)
                                                    {
                                                        EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                                                        var ftpDeleteFileRequest = CreateFtpRequest(archiveFileName, enableSsl, username, password, WebRequestMethods.Ftp.DeleteFile);
                                                        ftpDeleteFileRequest.UseBinary = true;

                                                        FtpWebResponse responseFileDelete = (FtpWebResponse)ftpDeleteFileRequest.GetResponse();
                                                    }

                                                    if (tries == 1)
                                                    {
                                                        CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerGenericUploadFileErrorMoveToArchiveDirectory, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, ex);

                                                        return;
                                                    }
                                                }
                                                finally
                                                {
                                                    if (resOldestUpload != null)
                                                        resOldestUpload.Close();
                                                    tries--;
                                                }
                                            } while (tries > 0);
                                        }
                                    }

                                    string fileShort = Path.GetFileName(fileName);
                                    string ftpPathToRootDir = url + "//" + directoryPath + fileShort;
                                    var ftpRequestUpload = CreateFtpRequest(ftpPathToRootDir, enableSsl, username, password, WebRequestMethods.Ftp.UploadFile);
                                    ftpRequestUpload.UseBinary = true;

                                    FtpWebResponse resUpload = null;

                                    try
                                    {
                                        lock (objFTPLock)
                                        {
                                            ftpRequestUpload.ContentLength = content.Length;

                                            Stream requestStream = ftpRequestUpload.GetRequestStream();
                                            requestStream.Write(content, 0, content.Length);
                                            requestStream.Close();

                                            resUpload = (FtpWebResponse)ftpRequestUpload.GetResponse();

                                            //No more looping
                                            nrTrialsTask = 0;
                                        }
                                    }
                                    catch (WebException ex)
                                    {
                                        CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerGenericUploadFileErrorToRootDirectory, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, ex);

                                        EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                                        return;
                                    }
                                    catch (Exception exex)
                                    {
                                        int trialCount = 3 - (nrTrialsTask) + 1;

                                        int trialDisplay = (nrTrialsTask > 0) ? trialCount : 3;

                                        var trial = $"Could not upload: {fileShort} on FTP Server at path: {ftpPathToRootDir}{Environment.NewLine}" +
                                                    $"Current upload trial number: {trialDisplay}";

                                        var invUpl = new FtpReportInvalidOperationException(trial, exex.InnerException);

                                        EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, invUpl);

                                        CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerGenericUploadFileErrorToRootDirectory, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, exex);

                                        return;
                                    }
                                    finally
                                    {
                                        resUpload?.Close();
                                    }
                                }
                                catch (WebException ex)
                                {
                                    EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                                    return;
                                }
                            }
                        }
                        catch (WebException webex)
                        {
                            CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerGenericUploadFileErrorListDirectoryContents, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, webex);

                            EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, webex);

                            return;
                        }
                        catch (Exception ex)
                        {
                            EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                            return;
                        }
                        finally
                        {
                            resListDir = null;
                        }
                    }
                    catch (Exception ex)
                    {
                        nrTrialsTask--;

                        if (nrTrialsTask == 0)
                        {
                            CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerGenericUploadFileErrorEmailPlatformSuppprt, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, ex);

                            EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                            return;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.ServerGenericServerUploadFileError, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, ex);

                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                return;
            }
        }

        private void EmailPlatformSupportWithErrorDetails(string username, string password, string serverAddress, int serverPort, string fileName, string customMessage, Exception ex)
        {
            if (ConfigurationManager.Instance.AppSettings["SmtpServer"] == null || ConfigurationManager.Instance.AppSettings["SmtpPort"] == null ||
                ConfigurationManager.Instance.AppSettings["SmtpUsername"] == null || ConfigurationManager.Instance.AppSettings["SmtpPassword"] == null)
            {
                var smtpex = new Exception("SMTP Server keys are missing!");

                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpServerNoGenerationEmailPlatformSupport, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, " secured password", smtpex);


                return;
            }

            string sendTo = ConfigurationManager.Instance.AppSettings["QAEmail"];

            var stackTrace = (ex.StackTrace != null && !string.IsNullOrWhiteSpace(ex.StackTrace)) ? ex.StackTrace : "None. ";

            StringBuilder emailMessage = new StringBuilder();

            string machineName = Environment.MachineName;

            emailMessage.Append(@"<h3>Failed to generate FTP Reports on Machine: ");
            emailMessage.Append(machineName + ". </h3>");
            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine("<div></div>");
            emailMessage.Append("<br>User is: ");
            emailMessage.Append(username + ". </br>");
            emailMessage.Append("<br>Host Server Address is: ");
            emailMessage.Append(serverAddress);
            emailMessage.Append(", Port: ");

            if (serverPort > 0)
            {
                emailMessage.Append(Convert.ToString(serverPort) + ".  ");
            }
            else
            {
                emailMessage.Append(" invalid port. ");
            }

            emailMessage.Append(" </br>");
            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine("<div></div>");

            emailMessage.Append("<br>Reason for Exception is: ");
            emailMessage.AppendLine("<div></div>");
            emailMessage.Append(ex.Message + ". </br>");

            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine("<div></div>");

            emailMessage.Append("<br>Exception Stack Trace is: ");
            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine(stackTrace + " </br>");

            if (ex.InnerException != null)
            {
                emailMessage.AppendLine("<br>Inner Exception Message is: ");
                emailMessage.AppendLine("<div></div>");
                emailMessage.AppendLine(ex.InnerException.Message + ". </br>");
            }

            if (ex.InnerException != null && ex.InnerException.StackTrace != null && !ex.InnerException.StackTrace.Equals(String.Empty))
            {
                emailMessage.AppendLine("<br>Inner exception Stack Trace is: ");
                emailMessage.AppendLine("<div></div>");
                emailMessage.AppendLine(ex.InnerException.StackTrace + ". </br>");
            }

            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine("<div></div>");

            if (!string.IsNullOrWhiteSpace(customMessage))
            {
                emailMessage.Append("<br>Custom message is: ");
                emailMessage.AppendLine("<div></div>");
                emailMessage.Append(customMessage + ". </br>");

                emailMessage.AppendLine("<div></div>");
                emailMessage.AppendLine("<div></div>");
            }

            if (ex.TargetSite != null)
            {
                emailMessage.AppendLine("<br>Method that throws the current exception (Target Site) is: ");
                emailMessage.AppendLine("<div></div>");
                emailMessage.AppendLine(ex.TargetSite.Name + ". </br>");
            }

            if (ex.InnerException != null && ex.InnerException.TargetSite != null)
            {
                emailMessage.AppendLine("<br>Inner exception Target Site is: ");
                emailMessage.AppendLine("<div></div>");
                emailMessage.AppendLine(ex.InnerException.TargetSite.Name + ". </br>");
            }

            emailMessage.AppendLine("<div></div>");

            string emailBodyMessage = emailMessage.ToString();

            string subject = "FTP Reports Generation Failed";
            string subjectShort = "FTP Reports Generation Failed on Machine: " + machineName + " for User:" + username + " at Host Server Address: " + serverAddress + ", Port: " + serverPort + "\r\n";

            try
            {
                var emailHelper = Sustainalytics.EmailHelper.EmailHelper.GetInstance();

                emailHelper.SendEmail(new List<string>(), null, subject, emailBodyMessage);
            }
            catch (SmtpException smtpex)
            {
                var smtpexNew = new SmtpException(subjectShort + "\r\n Failed to send email to: " + sendTo + "with FTP report generation error details. \r\n Error message is:" + smtpex.Message + ".  ");
                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, smtpexNew);

                return;
            }
            catch (Exception exex)
            {
                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, exex);

                return;
            }
        }

        private void EmailPlatformSupportWithErrorDetails(string username, string password, string serverAddress, int serverPort, string fileName, Exception ex)
        {
            if (ConfigurationManager.Instance.AppSettings["SmtpServer"] == null || ConfigurationManager.Instance.AppSettings["SmtpPort"] == null ||
                ConfigurationManager.Instance.AppSettings["SmtpUsername"] == null || ConfigurationManager.Instance.AppSettings["SmtpPassword"] == null)
            {
                var smtpex = new Exception("SMTP Server keys are missing!");
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpServerNoGenerationEmailPlatformSupport, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, " secured password", smtpex);


                return;
            }

            string sendTo = ConfigurationManager.Instance.AppSettings["QAEmail"];

            var stackTrace = (ex.StackTrace != null && !ex.StackTrace.Equals(String.Empty)) ? ex.StackTrace : "None. ";

            StringBuilder emailMessage = new StringBuilder();

            string machineName = Environment.MachineName;

            emailMessage.Append(@"<h3>Failed to generate FTP Reports on Machine: ");
            emailMessage.Append(machineName + ". </h3>");
            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine("<div></div>");
            emailMessage.Append("<br>User is: ");
            emailMessage.Append(username + ". </br>");
            emailMessage.Append("<br>Host Server Address is: ");
            emailMessage.Append(serverAddress);
            emailMessage.Append(", Port: ");

            if (serverPort > 0)
            {
                emailMessage.Append(Convert.ToString(serverPort) + ".  ");
            }
            else
            {
                emailMessage.Append(" invalid port. ");
            }

            emailMessage.Append(" </br>");
            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine("<div></div>");

            emailMessage.Append("<br>Reason for Exception is: ");
            emailMessage.AppendLine("<div></div>");
            emailMessage.Append(ex.Message + ". </br>");

            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine("<div></div>");

            emailMessage.Append("<br>Exception Stack Trace is: ");
            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine(stackTrace + " </br>");

            if (ex.InnerException != null)
            {
                emailMessage.AppendLine("<br>Inner Exception Message is: ");
                emailMessage.AppendLine("<div></div>");
                emailMessage.AppendLine(ex.InnerException.Message + ". </br>");
            }

            if (ex.InnerException != null && ex.InnerException.StackTrace != null && !ex.InnerException.StackTrace.Equals(String.Empty))
            {
                emailMessage.AppendLine("<br>Inner exception Stack Trace is: ");
                emailMessage.AppendLine("<div></div>");
                emailMessage.AppendLine(ex.InnerException.StackTrace + ". </br>");
            }

            emailMessage.AppendLine("<div></div>");
            emailMessage.AppendLine("<div></div>");

            if (ex.TargetSite != null)
            {
                emailMessage.AppendLine("<br>Method that throws the current exception (Target Site) is: ");
                emailMessage.AppendLine("<div></div>");
                emailMessage.AppendLine(ex.TargetSite.Name + ". </br>");
            }

            if (ex.InnerException != null && ex.InnerException.TargetSite != null)
            {
                emailMessage.AppendLine("<br>Inner exception Target Site is: ");
                emailMessage.AppendLine("<div></div>");
                emailMessage.AppendLine(ex.InnerException.TargetSite.Name + ". </br>");
            }

            emailMessage.AppendLine("<div></div>");

            string emailBodyMessage = emailMessage.ToString();

            string subject = "FTP Reports Generation Failed";
            string subjectShort = "FTP Reports Generation Failed on Machine: " + machineName + " for User:" + username + " at Host Server Address: " + serverAddress + ", Port: " + serverPort + "\r\n";

            try
            {
                var emailHelper = Sustainalytics.EmailHelper.EmailHelper.GetInstance();

                emailHelper.SendEmail(new List<string>(), null, subject, emailBodyMessage);
            }
            catch (SmtpException smtpex)
            {
                var smtpexNew = new SmtpException(subjectShort + "\r\n Failed to send email to: " + sendTo + "with FTP report generation error details. \r\n Error message is:" + smtpex.Message + ".  ");
                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, smtpexNew);

                return;
            }
            catch (Exception exex)
            {
                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, exex);

                return;
            }
        }

        public void UploadToFTPSSLServer(string username, string password, string serverAddress, int serverPort, string fileName, byte[] content, string directoryPath)
        {
            RemoteCertificateValidationCallback orgCallback = ServicePointManager.ServerCertificateValidationCallback;
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnValidateCertificate);
            ServicePointManager.Expect100Continue = true;

            try
            {
                UploadToFTPOrFPTSSLServerGeneric(username, password, serverAddress, serverPort, fileName, content, true, directoryPath);
                //UploadToFTPOrFPTSSLServerGenericNew(username, password, serverAddress, serverPort, fileName, content, true, directoryPath);
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpSSLServerUploadFileError, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, ex);

                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);
                return;
            }
            finally
            {
                ServicePointManager.ServerCertificateValidationCallback = orgCallback;
            }
        }

        public bool DoesFtpDirectoryExist(string dirPath, string username, string password)
        {
            FtpWebResponse dirExistsResponse = null;
            FtpWebRequest request = null;

            try
            {
                request = (FtpWebRequest)WebRequest.Create(dirPath);
                request.Method = WebRequestMethods.Ftp.ListDirectory;

                request.KeepAlive = false;
                request.UsePassive = true;
                request.Credentials = new NetworkCredential(username, password);

                dirExistsResponse = (FtpWebResponse)request.GetResponse();
                if (dirExistsResponse != null)
                {
                    return true;
                }
                return false;
            }
            catch (WebException)
            {
                return false;
            }
            finally
            {
                if (dirExistsResponse != null)
                {
                    dirExistsResponse.Close();
                }
            }
        }

        public void UploadToSSHFTPServerNew(string username, string password, string serverAddress, int serverPort, string fileName, byte[] content, string directoryPath)
        {
            var ftpSettings = new FtpSettings
            {
                Username = username,
                Password = password,
                Address = serverAddress,
                Port = serverPort,
                Protocol = Protocol.Sftp,
                KeyFile = fileName,
                Folder = directoryPath
            };

            var ftpClient = ftpClientFactory.TryCreate(ftpSettings);

            var bufferSize = ConfigurationManager.Instance.AppSettings.AppSetting<int>("BufferSize");
            var contentStream = new MemoryStream(content);

            string fileShort = Path.GetFileName(fileName);

            var filesOnServer = ftpClient.ftpClient.ListDirectory(directoryPath);

            if (filesOnServer != null && filesOnServer.Any())
            {
                if (!filesOnServer.Contains("Archive"))
                {
                    ftpClient.ftpClient.CreateDirectory(directoryPath + "Archive");
                }

                List<string> fileListOnRootPath = new List<string>();
                foreach (string fileItem in filesOnServer)
                {
                    fileListOnRootPath.Add(fileItem);
                }

                SortedDictionary<int, string> datesDictionary = GetSortedFilesDatesDictionary(fileName, fileListOnRootPath);

                if (datesDictionary.Any())
                {
                    string fileSenior = datesDictionary.First().Value;

                    ftpClient.ftpClient.RenameFile(directoryPath + fileSenior, directoryPath + "Archive/" + fileSenior);
                }
            }

            var ftpStream = ftpClient.ftpClient.GetRequestStream(fileShort);
            contentStream.CopyTo(ftpStream, bufferSize);
            ftpStream.Dispose();

        }

        public void UploadToSSHFTPServer(string username, string password, string serverAddress, int serverPort, string fileName, string directoryPath)
        {
            string urlAddress = serverAddress;

            int nrTrialsTask = 3;

            SshTransferProtocolBase sshCp = new Sftp(urlAddress, username, password);

            if (sshCp != null)
            {
                try
                {
                    string fileShort = Path.GetFileName(fileName);

                    while (nrTrialsTask != 0)
                    {
                        try
                        {
                            sshCp.Connect(serverPort);
                            if (sshCp.Connected)
                            {
                                var filesOnServer = ((Sftp)sshCp).GetFileList(directoryPath);

                                if (filesOnServer != null && filesOnServer.Any())
                                {
                                    if (!filesOnServer.Contains("Archive"))
                                    {
                                        ((Sftp)sshCp).Mkdir(directoryPath + "Archive");
                                    }

                                    List<string> fileListOnRootPath = new List<string>();
                                    foreach (string fileItem in filesOnServer)
                                    {
                                        fileListOnRootPath.Add(fileItem);
                                    }

                                    SortedDictionary<int, string> datesDictionary = GetSortedFilesDatesDictionary(fileName, fileListOnRootPath);

                                    if (datesDictionary.Any())
                                    {
                                        string fileSenior = datesDictionary.First().Value;

                                        ((Sftp)sshCp).Rename(directoryPath + fileSenior, directoryPath + "Archive/" + fileSenior);
                                    }
                                }

                                //Upload file
                                ((Sftp)sshCp).Put(fileName, directoryPath);

                                //No more looping if Put did not throw an exception
                                nrTrialsTask = 0;
                            }
                        }
                        catch (WebException exex)
                        {
                            int trialCount = 3 - (nrTrialsTask) + 1;

                            int trialDisplay = (nrTrialsTask > 0) ? trialCount : 3;

                            StringBuilder trialBuilder = new StringBuilder();

                            trialBuilder.Append("Could not upload: " + fileShort + " on FTP Server at path: " + urlAddress);
                            trialBuilder.Append(Environment.NewLine);
                            trialBuilder.Append("Current Upload trial number: ");
                            trialBuilder.Append(trialDisplay.ToString());

                            var invUpl = new FtpReportInvalidOperationException(trialBuilder.ToString(), exex.InnerException);

                            EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileShort, invUpl);

                            CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpSSHServerUploadFileErrorEmailPlatformSuppprt, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, exex);


                            return;
                        }
                        catch (Exception ex)
                        {
                            nrTrialsTask--;
                            if (nrTrialsTask == 0)
                            {
                                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpSSHServerUploadFileErrorEmailPlatformSuppprt, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, ex);


                                EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);
                            }
                            return;
                        }
                    }
                }
                catch (Exception ex)
                {
                    CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpSSHServerUploadFileError, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, fileName, ex);

                    EmailPlatformSupportWithErrorDetails(username, password, serverAddress, serverPort, fileName, ex);

                    return;
                }
                finally
                {
                    sshCp.Close();
                    if (sshCp != null)
                        sshCp = null;
                }
            }
        }

        private bool TestConnectionToFTPServers(ReportFTPDto ftpReportDto)
        {
            bool isValidFtpServerConnection;

            string userName = ftpReportDto.Username;
            string password = ftpReportDto.Password;

            string serverAddress = ftpReportDto.ServerAddress;
            int serverPort = ftpReportDto.ServerPort;

            switch (ftpReportDto.FtpServerType)
            {
                case FtpServerTypeEnum.Ftp:
                    {
                        isValidFtpServerConnection = IsConnectionToFTPServerAllowed(userName, password, serverAddress, serverPort);
                        break;
                    }
                case FtpServerTypeEnum.FtpSSH:
                    {
                        isValidFtpServerConnection = IsConnectionToSSHFTPServerAllowedNew(userName, password, serverAddress, serverPort);
                        //another method for FTP SSH Protocol
                        //isValidFtpServerConnection = IsConnectionToSSHFTPServerAllowed(userName, password, serverAddress, serverPort);
                        break;
                    }
                case FtpServerTypeEnum.FtpSSL:
                    {
                        //another method for FTP/SSL Protocol
                        isValidFtpServerConnection = IsConnectionToFTPSSLServerAllowed(userName, password, serverAddress, serverPort);
                        break;
                    }
                default:
                    {
                        var ex = new FtpReportServerAccessDeniedException("Timeout received from FTP Server or access denied on FTP Server at address :" + serverAddress + " and port: " + serverPort.ToString() + " for the entered credentials.");

                        EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, " No File Name", ex);

                        return false;
                    }
            }

            return isValidFtpServerConnection;
        }

        public void UpdateReportFtp(ReportFTPDto reportFtpModified)
        {
            string userName = reportFtpModified.Username;
            string password = reportFtpModified.Password;

            string serverAddress = reportFtpModified.ServerAddress;
            int serverPort = reportFtpModified.ServerPort;

            if (reportFtpModified.ServerPort == 0 || reportFtpModified.ServerAddress == null || reportFtpModified.ServerAddress.Equals(String.Empty) || (int)reportFtpModified.FtpServerType == 0)
            {
                var repEx = new FtpReportServerSettingsMissingException("Missing FTP Server Settings. Server Port is: " + reportFtpModified.ServerPort + " and Server Address Id is: " + reportFtpModified.ServerAddress + ", Ftp Server Type is: " + reportFtpModified.FtpServerType.ToString());

                CommonEventSerilog.Log.CallerError(FTPReportMessage.UpdateError, DebugHelper.GetCallerInfo(), reportFtpModified.Id, reportFtpModified.AccountId, reportFtpModified.ServerAddress, reportFtpModified.ServerPort, reportFtpModified.Username, reportFtpModified.Password, reportFtpModified.FtpServerType.ToString(), repEx);

                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, " No File Name", repEx);

                return;
            }

            if (reportFtpModified.Username == null || reportFtpModified.Username.Equals(String.Empty) || reportFtpModified.Password == null || reportFtpModified.Password.Equals(String.Empty))
            {
                var servEx = new FtpReportServerCredentialsMissingException("Missing FTP Server Credentials. User name is: " + reportFtpModified.Username + " and Password Id is: " + reportFtpModified.Password);

                CommonEventSerilog.Log.CallerError(FTPReportMessage.UpdateError, DebugHelper.GetCallerInfo(), reportFtpModified.Id, reportFtpModified.AccountId, reportFtpModified.ServerAddress, reportFtpModified.ServerPort, reportFtpModified.Username, reportFtpModified.Password, reportFtpModified.FtpServerType.ToString(), servEx);

                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, " No File Name", servEx);

                return;
            }

            Guid reportFtpId = reportFtpModified.Id;
            Guid accountId = reportFtpModified.AccountId;

            //Test connection to FTP Servers also for Update operations, since Server Address and Server Port are editable fields
            bool isValidFtpServerConnection = TestConnectionToFTPServers(reportFtpModified);

            if (!isValidFtpServerConnection)
            {
                var ex = new FtpReportServerAccessDeniedException("Access denied on FTP Server at address :" + serverAddress + " and port: " + serverPort.ToString() + " for the entered credentials.");
                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, " No File Name", ex);

                return;
            }
            try
            {
                List<ReportFTP> reportFtpEntities = _reportFtpRepository.ReadWhere(x => x.AccountId == accountId).ToList();

                if (reportFtpEntities.Any())
                {
                    ReportFTP reportFtpEntity = ReportFTPDto.ToReportFTPEntity(reportFtpModified);

                    //Encrypt the password
                    string encryptedPass = EncryptStringAES128(reportFtpModified.Password);
                    reportFtpEntity.Password = encryptedPass;
                    reportFtpEntity.AESKeyEncoded = Convert.ToBase64String(aes.Key.ToArray());
                    reportFtpEntity.AESInitVectorEncoded = Convert.ToBase64String(aes.IV.ToArray());

                    _reportFtpRepository.Update(reportFtpId, reportFtpEntity);
                }
                else
                {
                    var invalidEx = new FtpReportInvalidOperationException("The FTP Report marked for Update was not found.");

                    CommonEventSerilog.Log.CallerError(FTPReportMessage.UpdateError, DebugHelper.GetCallerInfo(), reportFtpModified.Id, reportFtpModified.AccountId, reportFtpModified.ServerAddress, reportFtpModified.ServerPort, reportFtpModified.Username, reportFtpModified.Password, reportFtpModified.FtpServerType.ToString(), invalidEx);

                    return;
                }
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.UpdateError, DebugHelper.GetCallerInfo(), reportFtpModified.Id, reportFtpModified.AccountId, reportFtpModified.ServerAddress, reportFtpModified.ServerPort, reportFtpModified.Username, reportFtpModified.Password, reportFtpModified.FtpServerType.ToString(), ex);

                EmailPlatformSupportWithErrorDetails(userName, password, serverAddress, serverPort, " No File Name", ex);
                return;
            }
        }

        public void DeleteReportFtpForAccountAndReportId(Guid accountId, Guid reportFtpId)
        {
            List<ReportFTP> reportFtpEntities = _reportFtpRepository.ReadWhere(x => x.AccountId == accountId && x.Id == reportFtpId).ToList();

            if (reportFtpEntities.Any())
            {
                _reportFtpRepository.Delete(reportFtpId);
            }
            else
            {
                var ftpEx = new FtpReportInvalidOperationException("The FTP Report marked for Delete was not found.");
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportDeleteError, DebugHelper.GetCallerInfo(), ftpEx);

                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ftpEx);

                return;
            }
        }

        public void DeleteReportFtpForAccount(Guid accountId)
        {
            List<ReportFTP> reportFtpEntities = _reportFtpRepository.ReadWhere(x => x.AccountId == accountId).ToList();

            if (reportFtpEntities.Any())
            {
                ReportFTP persistedFtpReportEntity = reportFtpEntities.FirstOrDefault();

                _reportFtpRepository.Delete(persistedFtpReportEntity.Id);
            }
            else
            {
                var ex = new FtpReportInvalidOperationException("The Account has no FTP Reports associated.");

                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);

                return;
            }
        }

        public void DeleteReportFtp(Guid reportFtpId)
        {
            try
            {
                List<ReportFTP> reportFtpEntities = _reportFtpRepository.ReadWhere(x => x.Id == reportFtpId).ToList();
                if (reportFtpEntities.Any())
                {
                    _reportFtpRepository.Delete(reportFtpId);
                }
                else
                {
                    var invalidEx = new FtpReportInvalidOperationException("The FTP Report marked for Delete was not found.");
                    CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportDeleteError, DebugHelper.GetCallerInfo(), reportFtpId, invalidEx);

                    EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", invalidEx);

                    return;
                }
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportDeleteError, DebugHelper.GetCallerInfo(), reportFtpId, ex);

                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                return;
            }
        }

        public bool IsConnectionToFTPServerAllowed(string username, string password, string serverAddress, int serverPort)
        {
            string url = AssembleValidUrlAddress(serverAddress, serverPort);

            FtpWebResponse res = null;
            bool isConnected = false;

            FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(url);
            ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
            ftpRequest.KeepAlive = false;
            ftpRequest.UsePassive = true;
            ftpRequest.Timeout = 120000;
            ftpRequest.Credentials = new NetworkCredential(username, password);

            try
            {
                res = (FtpWebResponse)ftpRequest.GetResponse();
                if (res != null)
                    isConnected = true;
            }
            catch (WebException webex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpServerAccessDeniedForCredentialsError, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, webex);

                isConnected = false;
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpServerAccessDeniedForCredentialsError, DebugHelper.GetCallerInfo(), serverAddress, serverPort, username, password, ex);

                isConnected = false;
            }
            finally
            {
                res.Close();
            }
            return isConnected;
        }

        public bool IsConnectionToFTPSSLServerAllowed(string username, string password, string serverAddress, int serverPort)
        {
            string url = AssembleValidUrlAddress(serverAddress, serverPort);

            FtpWebResponse res = null;
            bool isConnected = false;

            FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(url);

            ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
            ftpRequest.KeepAlive = false;
            ftpRequest.UsePassive = true;
            ftpRequest.EnableSsl = true;
            ftpRequest.Credentials = new NetworkCredential(username, password);

            RemoteCertificateValidationCallback orgCallback = ServicePointManager.ServerCertificateValidationCallback;

            try
            {
                // This statement is to ignore certification validation warning
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnValidateCertificate);
                ServicePointManager.Expect100Continue = true;

                // Connect to the server
                res = (FtpWebResponse)ftpRequest.GetResponse();
                if (res != null)
                    isConnected = true;
            }
            catch (WebException)
            {
                isConnected = false;
            }
            finally
            {
                ServicePointManager.ServerCertificateValidationCallback = orgCallback;
                if (res != null)
                {
                    res.Close();
                }
                else
                {
                    res = null;
                }
            }

            return isConnected;
        }

        private bool OnValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }

        public bool IsConnectionToSSHFTPServerAllowedNew(string username, string password, string serverAddress, int serverPort)
        {
            var ftpSettings = new FtpSettings
            {
                Username = username,
                Password = password,
                Address = serverAddress,
                Port = serverPort,
                Protocol = Protocol.Sftp
            };

            var ftpClient = ftpClientFactory.TryCreate(ftpSettings);

            return ftpClient.isConnected;
        }

        public bool IsConnectionToSSHFTPServerAllowed(string username, string password, string serverAddress, int serverPort)
        {
            string urlAddress = serverAddress;

            bool isConnected = false;

            SshTransferProtocolBase sshCp = new Sftp(urlAddress, username, password);

            if (sshCp != null)
            {
                try
                {
                    sshCp.Connect(serverPort);
                    if (sshCp.Connected)
                        isConnected = true;
                }
                catch (Exception)
                {
                    isConnected = false;
                }
                finally
                {
                    sshCp.Close();
                    if (sshCp != null)
                        sshCp = null;
                }
            }

            return isConnected;
        }

        #region FTPClientReport

        public List<FtpClientReport> LoadFtpClientReportsForAccount(Guid accountId)
        {
            if (accountId == null)
            {
                var ex = new FtpReportCriticalPropertiesMissingException("Missing critical property of FTP Client Report. FTP Client Report Account Id is: " + accountId);
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                return new List<FtpClientReport>();
            }

            List<FtpClientReport> clientFtpReportEntities = _ftpClientReportRepository.ReadWhere(x => x.AccountId == accountId).ToList();
            if (clientFtpReportEntities.Any())
            {
                return clientFtpReportEntities;
            }
            return new List<FtpClientReport>();
        }

        public FtpClientReport LoadFtpClientReport(Guid accountId, Guid reportId)
        {
            List<FtpClientReport> clientFtpReportEntities = _ftpClientReportRepository.ReadWhere(x => x.AccountId == accountId && x.ReportId == reportId).ToList();
            if (clientFtpReportEntities.Any())
            {
                return clientFtpReportEntities.FirstOrDefault();
            }
            return null;
        }

        public void AddFtpClientReport(FtpClientReportDto ftpClientReportDto)
        {
            if (ftpClientReportDto.Frequency == 0)
            {
                var clientRepEx = new FtpClientReportMandatoryDataMissingException("Missing FTP Client mandatory data. Frequency is: " + ftpClientReportDto.Frequency.ToString());
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportAddError, DebugHelper.GetCallerInfo(), ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, null, clientRepEx);

                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", clientRepEx);

                return;
            }

            try
            {
                //Assign NEW GUID, since the one from UI is hardcoded
                ftpClientReportDto.Id = Guid.NewGuid();

                FtpClientReport ftpClientReportEntity = FtpClientReportDto.ToFtpClientReportEntity(ftpClientReportDto);

                //LastGeneratedDate will only be updated from the ReportGeneration Engine based on timer..
                ftpClientReportEntity.LastGeneratedDate = null;

                DateTime? nextGenDate = UpdateOrInsertNextGenerationDate(ftpClientReportEntity.Frequency);

                if (nextGenDate.HasValue)
                {
                    ftpClientReportEntity.NextGenerationDate = nextGenDate.Value;

                    CommonEventSerilog.Log.Info(FTPReportMessage.FtpClientReportAdd, ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, ftpClientReportDto.Frequency.ToString());

                    _ftpClientReportRepository.Create(ftpClientReportEntity);

                    CommonEventSerilog.Log.Info(FTPReportMessage.FtpClientReportAddSuccess, ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, ftpClientReportDto.Frequency.ToString());

                }
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportAddError, DebugHelper.GetCallerInfo(), ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, ftpClientReportDto.Frequency.ToString(), ex);

                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                return;
            }
        }

        public void ModifyFtpClientReport(FtpClientReportDto ftpClientReportDto)
        {
            if (ftpClientReportDto.Frequency == 0)
            {
                var clientRepEx = new FtpClientReportMandatoryDataMissingException("Missing FTP Client mandatory data. Frequency is: " + ftpClientReportDto.Frequency);
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportUpdateError, DebugHelper.GetCallerInfo(), ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, null, clientRepEx);

                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", clientRepEx);
                return;
            }

            try
            {
                List<FtpClientReport> ftpClientReportEntities = _ftpClientReportRepository.ReadWhere(x => x.AccountId == ftpClientReportDto.AccountId && x.Id == ftpClientReportDto.Id).ToList();

                if (ftpClientReportEntities.Any())
                {
                    FtpClientReport entityClientReport = ftpClientReportEntities.FirstOrDefault();

                    DateTime? lastGeneratedTimePersisted = entityClientReport.LastGeneratedDate;

                    FtpClientReport ftpClientReportEntity = FtpClientReportDto.ToFtpClientReportEntity(ftpClientReportDto);

                    //Last Generated Date will be preserved from persisted Entity - it will not be updated
                    ftpClientReportEntity.LastGeneratedDate = lastGeneratedTimePersisted;

                    //Considering that Recurrence is Editable field, the Next Generation date must be updated, so it reflects the potentially changed Recurrence
                    DateTime? nextGenDate = UpdateOrInsertNextGenerationDate(ftpClientReportEntity.Frequency);

                    if (nextGenDate.HasValue)
                    {
                        ftpClientReportEntity.NextGenerationDate = nextGenDate.Value;

                        _ftpClientReportRepository.Update(ftpClientReportEntity.Id, ftpClientReportEntity);
                    }
                }
                else
                {
                    var invalidRepEx = new FtpReportInvalidOperationException("The FTP Client Report marked for Update was not found.");
                    CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportUpdateError, DebugHelper.GetCallerInfo(), ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, ftpClientReportDto.Frequency.ToString(), invalidRepEx);
                    EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", invalidRepEx);
                    return;
                }
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportUpdateError, DebugHelper.GetCallerInfo(), ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, ftpClientReportDto.Frequency.ToString(), ex);
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                return;
            }
        }

        public void ModifyFtpClientReportLastAndNextGeneratedDateToNow(FtpClientReport ftpClientReportDto)
        {
            if (ftpClientReportDto.Frequency == 0)
            {
                var clientRepEx = new FtpClientReportMandatoryDataMissingException("Missing FTP Client mandatory data. Frequency is: " + ftpClientReportDto.Frequency);
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportUpdateError, DebugHelper.GetCallerInfo(), ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, null, clientRepEx);

                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", clientRepEx);
                return;
            }

            try
            {
                List<FtpClientReport> ftpClientReportEntities = _ftpClientReportRepository.ReadWhere(x => x.AccountId == ftpClientReportDto.AccountId && x.Id == ftpClientReportDto.Id).ToList();

                if (ftpClientReportEntities.Any())
                {
                    FtpClientReport entityClientReport = ftpClientReportEntities.FirstOrDefault();

                    //Last Generated Date will be set to now - since it is from Report Generation Engine
                    entityClientReport.LastGeneratedDate = DateTime.UtcNow;

                    DateTime? nextGenerationDate = UpdateOrInsertNextGenerationDate(entityClientReport.Frequency);

                    if (nextGenerationDate.HasValue)
                    {
                        entityClientReport.NextGenerationDate = nextGenerationDate.Value;

                        CommonEventSerilog.Log.Info(FTPReportMessage.FtpClientReportUpdate, ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, ftpClientReportDto.Frequency.ToString());

                        _ftpClientReportRepository.Update(entityClientReport.Id, entityClientReport);

                        CommonEventSerilog.Log.Info(FTPReportMessage.FtpClientReportUpdateSuccess, ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, ftpClientReportDto.Frequency.ToString());

                    }
                }
                else
                {
                    var invalidRepEx = new FtpReportInvalidOperationException("The FTP Client Report marked for Update was not found.");
                    CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportUpdateError, DebugHelper.GetCallerInfo(), ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, ftpClientReportDto.Frequency.ToString(), invalidRepEx);

                    EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", invalidRepEx);

                    return;
                }
            }
            catch (Exception ex)
            {
                CommonEventSerilog.Log.CallerError(FTPReportMessage.FtpClientReportUpdateError, DebugHelper.GetCallerInfo(), ftpClientReportDto.Id, ftpClientReportDto.AccountId, ftpClientReportDto.ReportId, ftpClientReportDto.Frequency.ToString(), ex);
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);

                return;
            }
        }

        private DateTime? UpdateOrInsertNextGenerationDate(FrequencyEnum frequency)
        {
            DateTime nextGenerationDate;

            switch (frequency)
            {
                case FrequencyEnum.Daily:
                    {
                        nextGenerationDate = GetNextEligibleDayWithTimeUtc();
                        break;
                    }
                case FrequencyEnum.Weekly:
                    {
                        nextGenerationDate = GetNextEligibleSundayNextGenerationUtc(DateTime.UtcNow);
                        break;
                    }
                case FrequencyEnum.SemiMonthly:
                    {
                        nextGenerationDate = GetFirstEligibleOddSundayInMonthNextGenerationUtc(DateTime.UtcNow);
                        break;
                    }
                case FrequencyEnum.Monthly:
                    {
                        nextGenerationDate = GetFirstEligibleSundayInMonthNextGenerationUtc(DateTime.UtcNow);
                        break;
                    }
                case FrequencyEnum.Quarterly:
                    {
                        nextGenerationDate = GetFirstEligibleSundayInQuarterNextGenerationUtc(DateTime.UtcNow);
                        break;
                    }

                default:
                    {
                        var ex = new Exception("Invalid reccurence specified for FTP Report generation");

                        EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                        return null;
                    }

                    break;
            }

            return nextGenerationDate;
        }

        public void DeleteFtpClientReport(Guid accountId, Guid entityId)
        {
            List<FtpClientReport> reportFtpClientEntities = _ftpClientReportRepository.ReadWhere(x => x.AccountId == accountId && x.Id == entityId).ToList();

            if (reportFtpClientEntities.Any())
            {
                FtpClientReport persistedFtpClientReportEntity = reportFtpClientEntities.FirstOrDefault();

                _ftpClientReportRepository.Delete(persistedFtpClientReportEntity.Id);
            }
            else
            {
                var ex = new FtpReportInvalidOperationException("The Account has no FTP Client Reports associated.");
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);

                return;
            }
        }

        public void DeleteFtpClientReportsForAccount(Guid accountId)
        {
            List<FtpClientReport> reportFtpClientEntities = _ftpClientReportRepository.ReadWhere(x => x.AccountId == accountId).ToList();

            if (reportFtpClientEntities.Any())
            {
                List<Guid> entityIdsToBeDeleted = reportFtpClientEntities.Select(x => x.Id).ToList();

                _ftpClientReportRepository.Delete(entityIdsToBeDeleted);
            }
            else
            {
                var ex = new FtpReportInvalidOperationException("The Account has no FTP Client Reports associated.");
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);

                return;
            }
        }

        #endregion FTPClientReport

        public string EncryptStringAES128(string textToEncrypt)
        {
            if (textToEncrypt == null || textToEncrypt.Length <= 0)
            {
                var ex = new ArgumentNullException("Text to be Encrypted with AES128");
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                return null;
            }

            byte[] Key = aes.Key;
            byte[] initializationVector = aes.IV;

            if (Key == null || Key.Length <= 0)
            {
                var ex = new ArgumentNullException("AES128 Key");
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                return null;
            }
            if (initializationVector == null || initializationVector.Length <= 0)
            {
                var ex = new ArgumentNullException("AES128 Initialization Vector");
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);

                return null;
            }

            byte[] encrypted;

            using (AesManaged aesAlg = new AesManaged())
            {
                aesAlg.Key = Key;
                aesAlg.IV = initializationVector;

                try
                {
                    // Create a decrytor to perform the stream transform.

                    ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                    // Create the streams used for encryption.
                    using (MemoryStream msEncrypt = new MemoryStream())
                    {
                        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        {
                            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                            {
                                //Write all data to the stream.
                                swEncrypt.Write(textToEncrypt);
                            }
                            encrypted = msEncrypt.ToArray();
                        }
                    }
                }
                catch (Exception ex)
                {
                    EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                    return null;
                }
                finally
                {
                    if (aesAlg != null)
                        aesAlg.Clear();
                }
            }

            string termEncrypted = Convert.ToBase64String(encrypted.ToArray());

            return termEncrypted;
        }

        public string DecryptAES128(ReportFTP reportFTPEntity)
        {
            if (reportFTPEntity == null || reportFTPEntity.Password == null || reportFTPEntity.Password.Equals(String.Empty) || reportFTPEntity.AESKeyEncoded == null || reportFTPEntity.AESKeyEncoded.Equals(String.Empty)
                 || reportFTPEntity.AESInitVectorEncoded == null || reportFTPEntity.AESInitVectorEncoded.Equals(String.Empty))
            {
                var ex = new ArgumentNullException("ReportFTP Entity missing critical fields");
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                return null;
            }

            byte[] Key = Convert.FromBase64String(reportFTPEntity.AESKeyEncoded);
            byte[] initializationVector = Convert.FromBase64String(reportFTPEntity.AESInitVectorEncoded);

            if (Key == null || Key.Length <= 0)
            {
                var ex = new ArgumentNullException("AES128 Key");
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                return null;
            }
            if (initializationVector == null || initializationVector.Length <= 0)
            {
                var ex = new ArgumentNullException("AES128 Initialization Vector");
                EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                return null;
            }

            string plaintext = null;

            using (AesManaged aesAlg = new AesManaged())
            {
                aesAlg.Key = Key;
                aesAlg.IV = initializationVector;

                try
                {
                    // Create a decrytor to perform the stream transform.
                    ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                    byte[] encryptedBytes = Convert.FromBase64String(reportFTPEntity.Password);

                    // Create the streams used for decryption.
                    using (MemoryStream msDecrypt = new MemoryStream(encryptedBytes))
                    {
                        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                        {
                            using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                            {
                                // Read the decrypted bytes from the decrypting stream and place them in a string
                                plaintext = srDecrypt.ReadToEnd();
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    EmailPlatformSupportWithErrorDetails("No User Name", "No Password", "No Server Name", 0, " No File Name", ex);
                    return null;
                }
                finally
                {
                    if (aesAlg != null)
                        aesAlg.Clear();
                }

                return plaintext;
            }
        }
    }
}
